package me.chan.chatting.client;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;
import me.chan.chatting.server.handler.MyChannelDuplexHandler;
import me.chan.chatting.server.msg.ChatRequestMessage;
import me.chan.chatting.server.msg.GroupChatRequestMessage;
import me.chan.chatting.server.msg.GroupCreateRequestMessage;
import me.chan.chatting.server.msg.GroupJoinRequestMessage;
import me.chan.chatting.server.msg.GroupMembersRequestMessage;
import me.chan.chatting.server.msg.GroupQuitRequestMessage;
import me.chan.chatting.server.msg.LoginRequestMessage;
import me.chan.chatting.server.msg.LoginResponseMessage;
import me.chan.chatting.server.codec.ProtocolFrameDecoder;
import me.chan.chatting.server.codec.SharableMessageCodec;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
public class ChattingRoomClient {

    public static void main(String[] args) {

        CountDownLatch cdl = new CountDownLatch(1);
        AtomicBoolean LOGIN_FLAG = new AtomicBoolean(false);

        AtomicInteger loginCount = new AtomicInteger(0);

        NioEventLoopGroup group = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        try {
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 30000)
                    .handler(new ChannelInitializer<NioSocketChannel>() {
                        @Override
                        protected void initChannel(NioSocketChannel ch) throws Exception {
                            ch.pipeline()
                                    .addLast(new IdleStateHandler(0, 10, 0))
                                    // add heartbeat detector
                                    .addLast(new MyChannelDuplexHandler())
                                    .addLast(new ProtocolFrameDecoder())
                                    .addLast(new LoggingHandler(LogLevel.DEBUG))
                                    .addLast(new SharableMessageCodec())
                                    .addLast(new ChannelInboundHandlerAdapter() {


                                        @Override
                                        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                            if (msg instanceof LoginResponseMessage) {
                                                LoginResponseMessage message = (LoginResponseMessage) msg;
                                                if (message.isSuccess()) {
                                                    LOGIN_FLAG.set(true);
                                                    cdl.countDown();
                                                }
                                            }
                                        }

                                        @Override
                                        public void channelActive(ChannelHandlerContext ctx) throws Exception {
                                            new Thread(()-> {
                                                Scanner scanner = new Scanner(System.in);
                                                System.out.println("please input your username:");
                                                String username = scanner.nextLine();
                                                System.out.println("please input your password:");
                                                String password = scanner.nextLine();
                                                LoginRequestMessage msg = new LoginRequestMessage(username, password);
                                                ctx.writeAndFlush(msg);
                                                // waiting for the login result
                                                try {
                                                    cdl.await();
                                                } catch (InterruptedException e) {
                                                    log.error("client failed", e);
                                                }
                                                /*
                                                if (!LOGIN_FLAG.get()) {
                                                    loginCount.addAndGet(1);

                                                    if (loginCount.get() == 3) {
                                                        ctx.channel().close();
                                                        return ;
                                                    }

                                                    System.out.println("input error, please try again!");
                                                    System.out.println("please input your username:");
                                                    username = scanner.nextLine();
                                                    System.out.println("please input your password:");
                                                    password = scanner.nextLine();
                                                    msg = new LoginRequestMessage(username, password);
                                                    ctx.writeAndFlush(msg);

                                                }*/

                                                if (!LOGIN_FLAG.get()) {
                                                    ctx.channel().close();
                                                    return ;
                                                }


                                                repeat:
                                                while (true) {
                                                    System.out.println("==================================");
                                                    System.out.println("send [username] [content]");
                                                    System.out.println("gsend [group name] [content]");
                                                    System.out.println("gcreate [group name] [m1,m2,m3...]");
                                                    System.out.println("gmembers [group name]");
                                                    System.out.println("gjoin [group name]");
                                                    System.out.println("gquit [group name]");
                                                    System.out.println("quit");
                                                    System.out.println("==================================");
                                                    String command = scanner.nextLine();
                                                    String[] cmd = command.split(" ");
                                                    if (cmd.length == 0 || cmd.length != 3) {
                                                        System.out.println("error! please check your input.");
                                                        continue repeat;
                                                    }
                                                    String instruction = cmd[0];
                                                    switch (instruction) {
                                                        case "send":
                                                            String toUser = cmd[1];
                                                            String content = cmd[2];
                                                            ctx.writeAndFlush(new ChatRequestMessage(username, toUser, content));
                                                            break;
                                                        case "gsend":
                                                            String gsendGroup = cmd[1];
                                                            ctx.writeAndFlush(new GroupChatRequestMessage(username, gsendGroup, cmd[2]));
                                                            break;
                                                        case "gcreate":
                                                            String createGroup = cmd[1];
                                                            String[] members = cmd[2].split(",");
                                                            Set<String> groupMembers = new HashSet<>(Arrays.asList(members));
                                                            groupMembers.add(username);
                                                            ctx.writeAndFlush(new GroupCreateRequestMessage(createGroup, groupMembers));
                                                            break;
                                                        case "gmembers":
                                                            String groupName = cmd[1];
                                                            ctx.writeAndFlush(new GroupMembersRequestMessage(groupName));
                                                            break;
                                                        case "gjoin":
                                                            ctx.writeAndFlush(new GroupJoinRequestMessage(username, cmd[1]));
                                                            break;
                                                        case "gquit":
                                                            ctx.writeAndFlush(new GroupQuitRequestMessage(username, cmd[1]));
                                                            break;
                                                        case "quit":
                                                            ctx.channel().close();
                                                            return;

                                                    }
                                                }

                                            }, "client-input").start();
                                        }
                                    });

                        }
                    });
            Channel channel = bootstrap.connect("127.0.0.1", 8080).sync().channel();

           channel.closeFuture().sync();
        } catch (Exception ex) {
            log.error("client error", ex);
        } finally {
            group.shutdownGracefully();
        }
    }
}
